/*!
    \file  custom_hid_core.c
    \brief custom HID class driver
*/

/*
    Copyright (C) 2017 GigaDevice

    2017-02-10, V1.0.0, firmware for GD32F30x
*/

#include "usbd_int.h"
#include "custom_hid_core.h"

#define USBD_VID                     0x0416
#define USBD_PID                     0x5120

#define USBD_INTERFACE_HID_STR_IDX		USBD_INTERFACE_STR_IDX
#define USBD_INTERFACE_KEY_STR_IDX		(USBD_INTERFACE_HID_STR_IDX + 1)

uint8_t key_buf[8];
uint8_t report_buf[64];
uint8_t usbd_customhid_report_id = 0;
uint8_t flag = 0;
volatile uint8_t usb_customhid_tx_complete;
volatile uint8_t usb_keyhid_tx_complete;

static uint32_t usbd_customhid_altset = 0;
static uint32_t usbd_customhid_protocol = 0;
static uint32_t usbd_customhid_idlestate = 0;

usbd_int_cb_struct *usbd_int_fops = NULL;

void Bsp_HidRxCallback(uint8_t* buf, uint8_t len);

void (*rx_pack)(uint8_t* buf, uint8_t len);

/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */

/* USB custom HID device report descriptor */
const uint8_t customhid_report_descriptor[] =
{
    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    0x09, 0x00, // USAGE (0)
    0xA1, 0x01, // COLLECTION (Application)
    0x15, 0x00, //     LOGICAL_MINIMUM (0)
    0x25, 0xFF, //     LOGICAL_MAXIMUM (255)
    0x19, 0x01, //     USAGE_MINIMUM (1)
    0x29, 0x08, //     USAGE_MAXIMUM (8)
    0x95, 0x40, //     REPORT_COUNT (64)
    0x75, 0x08, //     REPORT_SIZE (8)
    0x81, 0x02, //     INPUT (Data,Var,Abs)
    0x19, 0x01, //     USAGE_MINIMUM (1)
    0x29, 0x08, //     USAGE_MAXIMUM (8)
    0x91, 0x02, //     OUTPUT (Data,Var,Abs)
    0xC0        // END_COLLECTION
};
	
/*!<USB HID Report Descriptor */
const uint8_t keyhid_report_descriptor[] =
{
    0x05, 0x01,     /* Usage Page(Generic Desktop Controls) */
    0x09, 0x06,     /* Usage(Keyboard) */
    0xA1, 0x01,     /* Collection(Application) */
    0x05, 0x07,     /* Usage Page(Keyboard/Keypad) */
    0x19, 0xE0,     /* Usage Minimum(0xE0) */
    0x29, 0xE7,     /* Usage Maximum(0xE7) */
    0x15, 0x00,     /* Logical Minimum(0x0) */
    0x25, 0x01,     /* Logical Maximum(0x1) */
    0x75, 0x01,     /* Report Size(0x1) */
    0x95, 0x08,     /* Report Count(0x8) */
    0x81, 0x02,     /* Input (Data) => Modifier byte */
    0x95, 0x01,     /* Report Count(0x1) */
    0x75, 0x08,     /* Report Size(0x8) */
    0x81, 0x01,     /* Input (Constant) => Reserved byte */
    0x95, 0x05,     /* Report Count(0x5) */
    0x75, 0x01,     /* Report Size(0x1) */
    0x05, 0x08,     /* Usage Page(LEDs) */
    0x19, 0x01,     /* Usage Minimum(0x1) */
    0x29, 0x05,     /* Usage Maximum(0x5) */
    0x91, 0x02,     /* Output (Data) => LED report */
    0x95, 0x01,     /* Report Count(0x1) */
    0x75, 0x03,     /* Report Size(0x3) */
    0x91, 0x01,     /* Output (Constant) => LED report padding */
    0x95, 0x06,     /* Report Count(0x6) */
    0x75, 0x08,     /* Report Size(0x8) */
    0x15, 0x00,     /* Logical Minimum(0x0) */
    0x25, 0x65,     /* Logical Maximum(0x65) */
    0x05, 0x07,     /* Usage Page(Keyboard/Keypad) */
    0x19, 0x00,     /* Usage Minimum(0x0) */
    0x29, 0x65,     /* Usage Maximum(0x65) */
    0x81, 0x00,     /* Input (Data) */
    0xC0            /* End Collection */
};


const usb_descriptor_device_struct device_descripter =
{
    .Header = 
     {
         .bLength = USB_DEVICE_DESC_SIZE, 
         .bDescriptorType = USB_DESCTYPE_DEVICE
     },
    .bcdUSB = 0x0200,
    .bDeviceClass = 0x00,
    .bDeviceSubClass = 0x00,
    .bDeviceProtocol = 0x00,
    .bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
    .idVendor = USBD_VID,
    .idProduct = USBD_PID,
    .bcdDevice = 0x0100,
    .iManufacturer = USBD_MFC_STR_IDX,
    .iProduct = USBD_PRODUCT_STR_IDX,
    .iSerialNumber = 0,
    .bNumberConfigurations = USBD_CFG_MAX_NUM
};
		 
#define CUSTOMHID_CONFIG_DESC_SIZE	\
	(	\
		sizeof(usb_descriptor_configuration_struct) + 	\
		sizeof(usb_descriptor_interface_struct) +		\
		sizeof(usb_hid_descriptor_hid_struct) +			\
		sizeof(usb_descriptor_endpoint_struct) * 2 		\
	)

usb_descriptor_configuration_set_struct configuration_descriptor = 
{
    .Config = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_configuration_struct), 
             .bDescriptorType = USB_DESCTYPE_CONFIGURATION 
         },
        .wTotalLength = CUSTOMHID_CONFIG_DESC_SIZE,
        .bNumInterfaces = 0x01,
        .bConfigurationValue = 0x01,
        .iConfiguration = 0x00,
        .bmAttributes = 0x80,
        .bMaxPower = 0x32
    },

    .HID_Interface = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_interface_struct), 
             .bDescriptorType = USB_DESCTYPE_INTERFACE 
         },
        .bInterfaceNumber = 0x00,
        .bAlternateSetting = 0x00,
        .bNumEndpoints = 0x02,
        .bInterfaceClass = 0x03,
        .bInterfaceSubClass = 0x00,
        .bInterfaceProtocol = 0x00,
        .iInterface = USBD_INTERFACE_HID_STR_IDX
    },

    .HID_VendorHID = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_hid_descriptor_hid_struct),
             .bDescriptorType = CUSTOMHID_DESC_TYPE 
         },
        .bcdHID = 0x0111,
        .bCountryCode = 0x00,
        .bNumDescriptors = 0x01,
        .bDescriptorType = CUSTOMHID_REPORT_DESCTYPE,
        .wDescriptorLength = sizeof(customhid_report_descriptor),
    },

    .HID_ReportINEndpoint = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct), 
             .bDescriptorType = USB_DESCTYPE_ENDPOINT 
         },
        .bEndpointAddress = CUSTOMHID_IN_EP,
        .bmAttributes = 0x03,
        .wMaxPacketSize = CUSTOMHID_IN_PACKET,
        .bInterval = 2,
    },

    .HID_ReportOUTEndpoint = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct), 
             .bDescriptorType = USB_DESCTYPE_ENDPOINT
         },
        .bEndpointAddress = CUSTOMHID_OUT_EP,
        .bmAttributes = 0x03,
        .wMaxPacketSize = CUSTOMHID_OUT_PACKET,
        .bInterval = 2,
    },
#if 0
/*------------------------KEY---------------------------------*/	
    .KEY_Interface = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_interface_struct), 
             .bDescriptorType = USB_DESCTYPE_INTERFACE 
         },
        .bInterfaceNumber = 0x01,
        .bAlternateSetting = 0x00,
        .bNumEndpoints = 0x02,
        .bInterfaceClass = 0x03,
        .bInterfaceSubClass = 0x00,
        .bInterfaceProtocol = 0x00,
        .iInterface = USBD_INTERFACE_KEY_STR_IDX
    },

    .KEY_VendorHID = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_hid_descriptor_hid_struct),
             .bDescriptorType = CUSTOMHID_DESC_TYPE 
         },
        .bcdHID = 0x0111,
        .bCountryCode = 0x00,
        .bNumDescriptors = 0x01,
        .bDescriptorType = CUSTOMHID_REPORT_DESCTYPE,
        .wDescriptorLength = sizeof(keyhid_report_descriptor),
    },

    .KEY_ReportINEndpoint = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct), 
             .bDescriptorType = USB_DESCTYPE_ENDPOINT 
         },
        .bEndpointAddress = KEYHID_IN_EP,
        .bmAttributes = 0x03,
        .wMaxPacketSize = KEYHID_IN_MAX_PACK_SIZE,
        .bInterval = 2,
    },

#if 0
    .KEY_ReportOUTEndpoint = 
    {
        .Header = 
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct), 
             .bDescriptorType = USB_DESCTYPE_ENDPOINT
         },
        .bEndpointAddress = KEYHID_OUT_EP,
        .bmAttributes = 0x03,
        .wMaxPacketSize = KEYHID_OUT_MAX_PACK_SIZE,
        .bInterval = 2,
    }
#endif 
#endif 

};

/* USB language ID Descriptor */
const usb_descriptor_language_id_struct usbd_language_id_desc = 
{
    .Header = 
     {
         .bLength = sizeof(usb_descriptor_language_id_struct), 
         .bDescriptorType = USB_DESCTYPE_STRING
     },
    .wLANGID = ENG_LANGID
};


void *const usbd_strings[7] = 
{
    [USBD_LANGID_STR_IDX] = (uint8_t *)&usbd_language_id_desc,
    [USBD_MFC_STR_IDX] = USBD_STRING_DESC("SHRFID"),
    [USBD_PRODUCT_STR_IDX] = USBD_STRING_DESC("SHBoot"),
    [USBD_SERIAL_STR_IDX] = USBD_STRING_DESC(""),
    
    [USBD_INTERFACE_HID_STR_IDX] = USBD_STRING_DESC("SHBoot"),
    [USBD_INTERFACE_KEY_STR_IDX] = USBD_STRING_DESC("SH100KEY"),
};


/*!
    \brief      initialize the HID device
    \param[in]  pudev: pointer to USB device instance
    \param[in]  config_index: configuration index
    \param[out] none
    \retval     USB device operation status
*/
usbd_status_enum custom_hid_init (void *pudev, uint8_t config_index)
{
    /* initialize Tx endpoint */
    usbd_ep_init(pudev, &(configuration_descriptor.HID_ReportINEndpoint));

    /* initialize Rx endpoint */
    usbd_ep_init(pudev, &(configuration_descriptor.HID_ReportOUTEndpoint));

    /* prepare receive data */
    usbd_ep_rx(pudev, CUSTOMHID_OUT_EP, report_buf, sizeof(report_buf));

#if 0
    /* initialize Tx endpoint */
    usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.KEY_ReportINEndpoint));

    /* initialize Rx endpoint */
    usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.KEY_ReportOUTEndpoint));
    /* prepare receive data */
    usbd_ep_rx(pudev, KEYHID_OUT_EP, key_buf, sizeof(key_buf));
#endif 
    return USBD_OK;
}

/*!
    \brief      de-initialize the HID device
    \param[in]  pudev: pointer to USB device instance
    \param[in]  config_index: configuration index
    \param[out] none
    \retval     USB device operation status
*/
usbd_status_enum  custom_hid_deinit (void *pudev, uint8_t config_index)
{
    /* deinitialize HID endpoints */
    usbd_ep_deinit (pudev, CUSTOMHID_IN_EP);
    usbd_ep_deinit (pudev, CUSTOMHID_OUT_EP);
#if 0
    usbd_ep_deinit (pudev, KEYHID_IN_EP);
    usbd_ep_deinit (pudev, KEYHID_OUT_EP);
#endif 
    return USBD_OK;
}

/*!
    \brief      handle the HID class-specific requests
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: device class-specific request
    \param[out] none
    \retval     USB device operation status
*/
usbd_status_enum custom_hid_req_handler (void *pudev, usb_device_req_struct *req)
{
    uint16_t len = 0;
    uint8_t *pbuf = NULL;
    uint8_t usbd_customhid_report_length = 0;

    switch (req->bmRequestType & USB_REQ_MASK) {
    case USB_CLASS_REQ:
        switch (req->bRequest) {
        case GET_REPORT:
            /* no use for this driver */
            break;
        case GET_IDLE:
            usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_idlestate, 1);
            break;
        case GET_PROTOCOL:
            usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_protocol, 1);
            break;
        case SET_REPORT:
            flag = 1;
            usbd_customhid_report_id = (uint8_t)(req->wValue);
            usbd_customhid_report_length = (uint8_t)(req->wLength);
            usbd_ep_rx (pudev, EP0_OUT, report_buf, usbd_customhid_report_length);
            break;
        case SET_IDLE:
            usbd_customhid_idlestate = (uint8_t)(req->wValue >> 8);
            break;
        case SET_PROTOCOL:
            usbd_customhid_protocol = (uint8_t)(req->wValue);
            break;
        default:
            usbd_enum_error (pudev, req);
            return USBD_FAIL;
        }
        break;
    case USB_STANDARD_REQ:
        /* standard device request */
        switch(req->bRequest) {
        case USBREQ_GET_DESCRIPTOR:
            switch(req->wValue >> 8) {
            case CUSTOMHID_REPORT_DESCTYPE:
				if(req->wIndex == 0){
	                len = MIN(sizeof(customhid_report_descriptor), req->wLength);
	                pbuf = (uint8_t *)customhid_report_descriptor;
				}
#if 0
				else{
					len = MIN(sizeof(keyhid_report_descriptor), req->wLength);
					pbuf = (uint8_t *)keyhid_report_descriptor;
				}
#endif 
                break;
            case CUSTOMHID_DESC_TYPE:
				if(req->wIndex == 0){
	                len = MIN(USB_CUSTOMHID_DESC_SIZE, req->wLength);
	                pbuf = (uint8_t *)(&(configuration_descriptor.HID_VendorHID));
				}
#if 0
				else{
					len = MIN(USB_CUSTOMHID_DESC_SIZE, req->wLength);
					pbuf = (uint8_t *)(&(configuration_descriptor.KEY_VendorHID));
				}
#endif 
                break;
            default:
                break;
            }
            usbd_ep_tx (pudev, EP0_IN, pbuf, len);
            break;
        case USBREQ_GET_INTERFACE:
            usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_altset, 1);
            break;
        case USBREQ_SET_INTERFACE:
            usbd_customhid_altset = (uint8_t)(req->wValue);
            break;
        }
        break;
    }

    return USBD_OK;
}

/*!
    \brief      handle data stage
    \param[in]  pudev: pointer to USB device instance
    \param[in]  rx_tx: data transfer direction:
      \arg        USBD_TX
      \arg        USBD_RX
    \param[in]  ep_id: endpoint identifier
    \param[out] none
    \retval     USB device operation status
*/
usbd_status_enum  custom_hid_data_handler (void *pudev, usbd_dir_enum rx_tx, uint8_t ep_id)
{
    if((USBD_TX == rx_tx) && ((CUSTOMHID_IN_EP & 0x7F) == ep_id)) {
		usb_customhid_tx_complete = 1;
        return USBD_OK;
    } else if ((USBD_RX == rx_tx) && (CUSTOMHID_OUT_EP == ep_id)) {
		uint32_t count = usbd_rx_count_get(pudev, ep_id);
		Bsp_HidRxCallback(report_buf, count);
        usbd_ep_rx(pudev, CUSTOMHID_OUT_EP, report_buf, sizeof(report_buf));
//			custom_hid_report_send(pudev, report_buf, sizeof(report_buf));
        return USBD_OK;
    }
	else if((USBD_TX == rx_tx) && ((KEYHID_IN_EP & 0x7F) == ep_id)){
		usb_keyhid_tx_complete = 1;
		return USBD_OK;
	}
	else if ((USBD_RX == rx_tx) && (KEYHID_OUT_EP == ep_id)) {
		usbd_ep_rx(pudev, KEYHID_OUT_EP, key_buf, sizeof(key_buf));
        return USBD_OK;
    }
    return USBD_FAIL;
}

/*!
    \brief      send custom HID report
    \param[in]  pudev: pointer to USB device instance
    \param[in]  report: pointer to HID report
    \param[in]  len: data length
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  custom_hid_report_send (usbd_core_handle_struct *pudev, uint8_t *report, uint16_t len)
{
	volatile uint32_t i = 2 * 1000 * 1000;
	usb_customhid_tx_complete = 0;
    usbd_ep_tx (pudev, CUSTOMHID_IN_EP, report, len);
	while(usb_customhid_tx_complete == 0){
		if(--i == 0){
			return USBD_FAIL;
		}
	}
    return USBD_OK;
}

uint8_t key_hid_report_send(usbd_core_handle_struct *pudev, uint8_t *report, uint16_t len){
	volatile uint32_t i = 3 * 1000 * 1000;
	usb_keyhid_tx_complete = 0;
    usbd_ep_tx (pudev, KEYHID_IN_EP, report, len);
	while(usb_keyhid_tx_complete == 0){
		if(--i == 0){
			return USBD_FAIL;
		}
	}
    return USBD_OK;
}


void custom_hid_set_callback(void(*cb)(uint8_t*, uint8_t)){
	rx_pack = cb;
}


